package link.luyu.plugin.brochain.types;

import java.util.stream.Collectors;
import link.luyu.plugin.brochain.web3.methods.response.Block;
import link.luyu.plugin.brochain.web3.methods.response.Page;
import link.luyu.plugin.brochain.web3.methods.response.Votes;
import org.web3j.rlp.RlpDecoder;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
import org.web3j.utils.Numeric;

public class BlockAndPage {

    private Block block;
    private Page page;
    private Votes votes;

    public BlockAndPage(Block block, Page page) {
        this.block = block;
        this.page = page;
    }

    public Block getBlock() {
        return this.block;
    }

    public Page getPage() {
        return this.page;
    }

    public Votes getVotes() {
        return this.votes;
    }

    public void setVotes(Votes votes) {
        this.votes = votes;
    }

    public byte[] serialize() {
        RlpList blockHeader =
                new RlpList(
                        RlpString.create(Numeric.hexStringToByteArray(block.getParentHash())),
                        RlpString.create(Numeric.hexStringToByteArray(block.getStateRoot())),
                        RlpString.create(Numeric.hexStringToByteArray(block.getTransactionsRoot())),
                        RlpString.create(Numeric.hexStringToByteArray(block.getPagesRoot())),
                        RlpString.create(Numeric.hexStringToByteArray(block.getReceiptsRoot())),
                        RlpString.create(block.getNumber()),
                        RlpString.create(block.getTimestamp()),
                        RlpString.create(Numeric.hexStringToByteArray(block.getExtraData())));
        RlpList regionPages =
                new RlpList(
                        block.getRegionPages()
                                .stream()
                                .map(
                                        regionPage ->
                                                new RlpList(
                                                        RlpString.create(
                                                                Numeric.hexStringToByteArray(
                                                                        regionPage.getRegionId())),
                                                        RlpString.create(
                                                                Numeric.hexStringToByteArray(
                                                                        regionPage.getPageHash()))))
                                .collect(Collectors.toList()));
        RlpList pageHeader =
                page == null
                        ? new RlpList()
                        : new RlpList(
                                RlpString.create(Numeric.hexStringToByteArray(page.getRegionId())),
                                RlpString.create(Numeric.hexStringToByteArray(page.getStateRoot())),
                                RlpString.create(
                                        Numeric.hexStringToByteArray(page.getTransactionsRoot())),
                                RlpString.create(
                                        Numeric.hexStringToByteArray(page.getParentHash())),
                                RlpString.create(page.getBlockNumber()),
                                RlpString.create(page.getTimestamp()));
        RlpList pageTransactions =
                page == null
                        ? new RlpList()
                        : new RlpList(
                                page.getTransactions()
                                        .stream()
                                        .map(
                                                transaction ->
                                                        new RlpList(
                                                                RlpString.create(
                                                                        transaction.getNonce()),
                                                                RlpString.create(
                                                                        transaction
                                                                                .getBlockLimit()),
                                                                RlpString.create(
                                                                        Numeric
                                                                                .hexStringToByteArray(
                                                                                        transaction
                                                                                                                .getTo()
                                                                                                        != null
                                                                                                ? transaction
                                                                                                        .getTo()
                                                                                                : "")),
                                                                RlpString.create(
                                                                        Numeric
                                                                                .hexStringToByteArray(
                                                                                        transaction
                                                                                                .getInput()))))
                                        .collect(Collectors.toList()));
        return RlpEncoder.encode(
                new RlpList(blockHeader, regionPages, pageHeader, pageTransactions));
    }

    public static BlockAndPage deserialize(byte[] data) {
        RlpList list = RlpDecoder.decode(data);
        // TODO
        return new BlockAndPage(new Block(), new Page());
    }

    @Override
    public String toString() {
        StringBuilder builder =
                new StringBuilder("BlockAndPage")
                        .append(" { blockNumber=")
                        .append(block.getNumber().toString(10))
                        .append(", blockHash='")
                        .append(block.getHash())
                        .append('\'');
        if (page != null) {
            builder.append(", regionId='")
                    .append(page.getRegionId())
                    .append('\'')
                    .append(", pageHash='")
                    .append(page.getHash())
                    .append('\'');
        }
        builder.append(" }");
        return builder.toString();
    }
}
